package com.molichuxing.framework.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisSentinelPool;
import redis.clients.jedis.ScanParams;
import redis.clients.jedis.ScanResult;

import java.util.*;

/**
 * redis工具类
 * 
 * @author zoumingyu
 * @date 2019年8月25日
 *
 */
public class RedisUtil {
	private final Logger logger = LoggerFactory.getLogger(RedisUtil.class);

	private JedisSentinelPool jedisSentinelPool;

	public void setJedisSentinelPool(JedisSentinelPool jedisSentinelPool) {
		this.jedisSentinelPool = jedisSentinelPool;
	}

	public enum RedisNxxx {
		/**
		 * key不存在进行set
		 */
		NX("NX"),
		/**
		 * key存在进行set
		 */
		XX("XX");

		private String value;

		//构造方法
		RedisNxxx(String value) {
			this.value = value;
		}

		public String getValue() {
			return value;
		}
	}

	public enum RedisExpx {
		/**
		 * 秒
		 */
		EX("EX"),
		/**
		 * 毫秒
		 */
		PX("PX");

		private String value;

		//构造方法
		RedisExpx(String value) {
			this.value = value;
		}

		public String getValue() {
			return value;
		}
	}


	/**
	 * 获取Jedis实例
	 *
	 * @return Jedis
	 */
	private Jedis getJedis() {
		Jedis jedis = null;
		try {
			if (jedisSentinelPool != null) {
				jedis = jedisSentinelPool.getResource();
			}
		} catch (Exception e) {
			logger.error("Jedis实例获取异常:", e);
			if (jedis != null) {
				jedis.close();
			}
		}
		return jedis;
	}

	/**
	 * 设置值-不带过期时间
	 *
	 * @param key
	 * @param value
	 * @return OK：操作成功 <br/>
	 *         null：操作失败
	 */
	public String set(String key, String value) {
		String result = null;
		Jedis jedis = getJedis();
		try {
			result = jedis.set(key, value);
		} catch (Exception e) {
			logger.error("设置值失败：" + e.getMessage(), e);
		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}

		return result;
	}

	/**
	 * 设置值-带过期时间
	 *
	 * @param key
	 * @param value
	 * @param expire
	 * @return
	 */
	public String setex(String key, String value, int expire) {
		String result = null;
		Jedis jedis = getJedis();
		try {
			result = jedis.setex(key, expire, value);
		} catch (Exception e) {
			logger.error("设置值失败：" + e.getMessage(), e);
			throw e;
		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}

		return result;
	}


	/**
	 * 设置值-带过期时间
	 *
	 * @param key
	 * @param value
	 * @param nxxx  只能取NX或者XX，如果取NX，则只有当key不存在是才进行set，如果取XX，则只有当key已经存在时才进行set
	 * @param expx  只能取EX或者PX，代表数据过期时间的单位，EX代表秒，PX代表毫秒。
	 * @param time  过期时间，单位是expx所代表的单位。
	 * @return OK：操作成功 <br/> null：操作失败
	 */
	public String set(final String key, final String value, final RedisNxxx nxxx, final RedisExpx expx,
	                  final int time) {
		String result = null;
		Jedis jedis = getJedis();
		try {
			result = jedis.set(key, value, nxxx.getValue(), expx.getValue(), time);
		} catch (Exception e) {
			logger.error("设置值失败：" + e.getMessage(), e);
		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}

		return result;
	}

	/**
	 * 获取值
	 *
	 * @param key
	 * @return
	 */
	public String get(String key) {
		String result = null;
		Jedis jedis = getJedis();
		try {
			result = jedis.get(key);
		} catch (Exception e) {
			logger.error("获取值失败：" + e.getMessage(), e);
		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}

		return result;
	}

	/**
	 * 设置对象
	 * 
	 * @param key
	 * @param obj
	 * @return
	 * 
	 */
	public <T> String setObject(String key, T obj) {
		String result = null;
		Jedis jedis = getJedis();

		try {
			byte[] data = SerializeUtil.serialize(obj);
			result = jedis.set(key.getBytes(), data);
		} catch (Exception e) {
			logger.error("设置对象失败：" + e.getMessage(), e);

		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}

		return result;
	}

	/**
	 * 获取对象
	 * 
	 * @param key
	 * @return
	 * 
	 */
	@SuppressWarnings("unchecked")
	public <T> T getObject(String key) {
		T result = null;
		Jedis jedis = getJedis();

		try {
			byte[] data = jedis.get(key.getBytes());
			if (data != null && data.length > 0) {
				result = (T) SerializeUtil.unserialize(data);
			}
		} catch (Exception e) {
			logger.error("获取对象失败：" + e.getMessage(), e);

		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}

		return result;
	}

	/**
	 * 设置key的过期时间
	 * 
	 * @param key
	 * @param expire
	 *             过期时间，单位：秒
	 * @return
	 */
	public long expire(String key, int expire) {
		long result = 0L;
		Jedis jedis = getJedis();
		try {
			result = jedis.expire(key, expire);
		} catch (Exception e) {
			logger.error(String.format("设置key=%s的过期时间失败：" + e.getMessage(), key), e);

		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}

		return result;
	}

	/**
	 * 判断key是否存在
	 * 
	 * @param key
	 * @return
	 * 
	 */
	public boolean exists(String key) {
		boolean result = false;
		Jedis jedis = getJedis();
		try {
			result = jedis.exists(key);
		} catch (Exception e) {
			logger.error(String.format("判断key=%s是否存在失败：" + e.getMessage(), key), e);

		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}

		return result;
	}

	/**
	 * 删除key
	 * 
	 * @param keys
	 * @return
	 * 
	 */
	public long del(String keys) {
		long result = 0L;
		Jedis jedis = getJedis();

		try {
			result = jedis.del(keys);
		} catch (Exception e) {
			logger.error(String.format("删除key=%s失败：" + e.getMessage(), keys), e);

		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}

		return result;
	}

	/**
	 * set if not exists，若key已存在，则setnx不做任何操作
	 * 
	 * @param key
	 * @param value
	 * @return
	 * 
	 */
	public long setnx(String key, String value) {
		long result = 0L;
		Jedis jedis = getJedis();

		try {
			result = jedis.setnx(key, value);
		} catch (Exception e) {
			logger.error("设置值失败：" + e.getMessage(), e);

		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}

		return result;
	}

	/**
	 * set if not exists，若key已存在，则setnx不做任何操作
	 * 
	 * @param key
	 * @param value
	 * @param expire
	 *            过期时间，单位：秒
	 * @return
	 * 
	 */
	public long setnx(String key, String value, int expire) {
		long result = 0L;

		Jedis jedis = getJedis();

		try {
			result = jedis.setnx(key, value);
			jedis.expire(key, expire);
		} catch (Exception e) {
			logger.error("设置值失败：" + e.getMessage(), e);

		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}

		return result;
	}

	/**
	 * 在列表key的头部插入元素
	 * 
	 * @param key
	 * @param values
	 * @return
	 * 
	 */
	public long lpush(String key, String... values) {
		long result = 0L;

		Jedis jedis = getJedis();

		try {
			result = jedis.lpush(key, values);
		} catch (Exception e) {
			logger.error("在列表key的头部插入元素失败：" + e.getMessage(), e);

		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}

		return result;
	}

	/**
	 * 在列表key的尾部插入元素
	 * 
	 * @param key
	 * @param values
	 * @return
	 * 
	 */
	public long rpush(String key, String... values) {
		long result = 0L;
		Jedis jedis = getJedis();

		try {
			result = jedis.rpush(key, values);
		} catch (Exception e) {
			logger.error("在列表key的尾部插入元素失败：" + e.getMessage(), e);

		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}

		return result;
	}

	/**
	 * 返回存储在key列表的特定元素
	 * 
	 * 
	 * @param key
	 * @param start
	 *            开始索引，索引从0开始，0表示第一个元素，1表示第二个元素
	 * @param end
	 *            结束索引，-1表示最后一个元素，-2表示倒数第二个元素
	 * @return
	 * 
	 */
	public List<String> lrange(String key, long start, long end) {
		List<String> result = null;
		Jedis jedis = getJedis();

		try {
			result = jedis.lrange(key, start, end);
		} catch (Exception e) {
			logger.error("查询列表元素失败：" + e.getMessage(), e);

		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}

		return result;
	}

	/**
	 * 获取列表长度
	 * 
	 * @param key
	 * @return
	 * 
	 */
	public long llen(String key) {
		long result = 0;
		Jedis jedis = getJedis();

		try {
			result = jedis.llen(key);
		} catch (Exception e) {
			logger.error("获取列表长度失败：" + e.getMessage(), e);

		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}

		return result;
	}

	/**
	 * 移除等于value的元素<br/>
	 * <br/>
	 * 当count>0时，从表头开始查找，移除count个；<br/>
	 * 当count=0时，从表头开始查找，移除所有等于value的；<br/>
	 * 当count<0时，从表尾开始查找，移除count个
	 * 
	 * @param key
	 * @param count
	 * @param value
	 * @return
	 * 
	 */
	public long lrem(String key, long count, String value) {
		long result = 0;
		Jedis jedis = getJedis();

		try {
			result = jedis.lrem(key, count, value);
		} catch (Exception e) {
			logger.error("获取列表长度失败：" + e.getMessage(), e);

		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}

		return result;
	}

	/**
	 * 对列表进行修剪
	 * 
	 * @param key
	 * @param start
	 * @param end
	 * @return OK：命令执行成功
	 * 
	 */
	public String ltrim(String key, long start, long end) {
		String result = null;
		Jedis jedis = getJedis();

		try {
			result = jedis.ltrim(key, start, end);
		} catch (Exception e) {
			logger.error("获取列表长度失败：" + e.getMessage(), e);

		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}

		return result;
	}

	/**
	 * 设置List集合（慎用）
	 * 
	 * @param key
	 * @param dataList
	 * @return
	 * 
	 */
	public synchronized <T> T setList(String key, List<T> dataList) {
		Jedis jedis = getJedis();
		if (jedis == null) {
			return null;
		}

		T result = null;
		try {
			List<T> list = getList(key);
			if (!CollectionUtils.isEmpty(list)) {
				dataList.addAll(list);
			}

			if (!CollectionUtils.isEmpty(dataList)) {
				byte[] data = SerializeUtil.serializeList(dataList);
				jedis.set(key.getBytes(), data);
			} else {// 如果list为空,则设置一个空
				jedis.set(key.getBytes(), "".getBytes());
			}
		} catch (Exception e) {
			logger.error("设置List集合失败：" + e.getMessage(), e);

		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}

		return result;
	}

	/**
	 * 获取List集合（慎用）
	 * 
	 * @param key
	 * @return
	 * 
	 */
	@SuppressWarnings("unchecked")
	public <T> List<T> getList(String key) {
		Jedis jedis = getJedis();
		if (jedis == null) {
			return null;
		}

		try {
			byte[] data = getJedis().get(key.getBytes());
			if (data != null && data.length > 0) {
				return (List<T>) SerializeUtil.unserializeList(data);
			}
		} catch (Exception e) {
			logger.error("获取List集合失败：" + e.getMessage(), e);

		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}

		return null;
	}

	/**
	 * 缓存Map赋值
	 * 
	 * @param key
	 * @param field
	 * @param value
	 * @return
	 * 
	 */
	public long hset(String key, String field, String value) {
		long result = 0L;
		Jedis jedis = getJedis();

		try {
			result = jedis.hset(key, field, value);
		} catch (Exception e) {
			logger.error("缓存Map赋值失败：" + e.getMessage(), e);

		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}

		return result;
	}

	/**
	 * 获取缓存的Map值
	 * 
	 * @param key
	 * @return
	 */
	public String hget(String key, String field) {
		String result = null;
		Jedis jedis = getJedis();

		try {
			result = jedis.hget(key, field);
		} catch (Exception e) {
			logger.error("获取缓存的Map值失败：" + e.getMessage(), e);

		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}

		return result;
	}

	/**
	 * 获取map所有的字段和值
	 * 
	 * @param key
	 * @return
	 * 
	 */
	public Map<String, String> hgetAll(String key) {
		Map<String, String> map = new HashMap<String, String>();
		Jedis jedis = getJedis();

		try {
			map = jedis.hgetAll(key);
		} catch (Exception e) {
			logger.error("获取map所有的字段和值失败：" + e.getMessage(), e);

		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}

		return map;
	}

	/**
	 * 查看哈希表 key 中，指定的field字段是否存在。
	 * 
	 * @param key
	 * @param field
	 * @return
	 * 
	 */
	public Boolean hexists(String key, String field) {
		Jedis jedis = getJedis();

		try {
			return jedis.hexists(key, field);
		} catch (Exception e) {
			logger.error("查看哈希表field字段是否存在失败：" + e.getMessage(), e);

		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}

		return null;
	}

	/**
	 * 获取所有哈希表中的字段
	 * 
	 * @param key
	 * @return
	 * 
	 */
	public Set<String> hkeys(String key) {
		Set<String> set = null;
		Jedis jedis = getJedis();

		try {
			set = jedis.hkeys(key);
		} catch (Exception e) {
			logger.error("获取所有哈希表中的字段失败：" + e.getMessage(), e);

		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}

		return set;
	}

	/**
	 * 获取所有哈希表中的值
	 * 
	 * @param key
	 * @return
	 * 
	 */
	public List<String> hvals(String key) {
		List<String> list = null;
		Jedis jedis = getJedis();

		try {
			list = jedis.hvals(key);
		} catch (Exception e) {
			logger.error("获取所有哈希表中的值失败：" + e.getMessage(), e);

		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}

		return list;
	}

	/**
	 * 从哈希表 key 中删除指定的field
	 * 
	 * @param key
	 * @param field
	 * @return
	 * 
	 */
	public long hdel(String key, String... fields) {
		Jedis jedis = getJedis();

		try {
			return jedis.hdel(key, fields);
		} catch (Exception e) {
			logger.error("map删除指定的field失败：" + e.getMessage(), e);

		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}

		return 0;
	}

	public Set<String> keys(String pattern) {
		Set<String> keyList = null;
		Jedis jedis = getJedis();

		try {
			keyList = jedis.keys(pattern);
		} catch (Exception e) {
			logger.error("操作keys失败：" + e.getMessage(), e);

		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}

		return keyList;
	}


	public String rpop(String key) {
		String result = null;
		Jedis jedis = getJedis();

		try {
			result = jedis.rpop(key);
		} catch (Exception e) {
			logger.error("操作keys失败：" + e.getMessage(), e);

		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}

		return result;
	}


	/**
	 * 设置List缓存，如果该key值已经存在，覆盖原来的值
	 * @param key 键
	 * @param value 值
	 * @param cacheSeconds 超时时间，0为不超时
	 * @return
	 */
	public  long setObjectList(String key, List<?> value, int cacheSeconds) {
		long result = 0;
		Jedis jedis = null;
		try {
			jedis = getJedis();
			if (jedis.exists(key)) {
				jedis.del(key);
			}
			for(int i=0;i<value.size();i++){
				jedis.rpush(SerializeUtil.serialize(key), SerializeUtil.serialize(value.get(i)));
			}
			if (cacheSeconds != 0) {
				jedis.expire(key, cacheSeconds);
			}
			logger.debug("setObjectList {} = {}", key, value);
		} catch (Exception e) {
			logger.error("setObjectList {} = {}", key, value, e);
		} finally {
			jedis.close();
		}
		return result;
	}


	/**
	 * 设置List缓存，如果该key值原来就 存在，覆盖原来的
	 * @param key 键
	 * @param value 值
	 * @param cacheSeconds 超时时间，0为不超时
	 * @return
	 */
	public long setList(String key, List<String> value, int cacheSeconds) {
		long result = 0;
		Jedis jedis = null;
		try {
			jedis = getJedis();
			if (jedis.exists(key)) {
				jedis.del(key);
			}
			result = jedis.rpush(key, (String []) value.toArray(new String[value.size()]));
			if (cacheSeconds != 0) {
				jedis.expire(key, cacheSeconds);
			}
			logger.debug("setList {} = {}", key, value);
		} catch (Exception e) {
			logger.warn("setList {} = {}", key, value, e);
		} finally {
			jedis.close();
		}
		return result;
	}

	public List<String> scan(String match) {
		List<String> list = new ArrayList<>();
		Jedis jedis = getJedis();
		try {

			String cursor = ScanParams.SCAN_POINTER_START;

			ScanParams scanParams = new ScanParams();
			scanParams.match(match);
			scanParams.count(1000);
			do {
				ScanResult<String> scanResult = jedis.scan(cursor, scanParams);
				list.addAll(scanResult.getResult());
				cursor = scanResult.getStringCursor();
			} while (!"0".equals(cursor));
		} catch (Exception e) {
			logger.info("redis正则查询失败");
		} finally {
			jedis.close();
		}
		return list;
	}

	public String hmset(String key, Map<String, String> map) {
		if (!map.isEmpty()) {
			Jedis jedis = getJedis();
			String result=jedis.hmset(key,map);
			jedis.close();
			return result;
		}
		return null;
	}

	public Long incr(String key) {
		Long result = null;
		Jedis jedis = getJedis();

		try {
			result = jedis.incr(key);
		} catch (Exception e) {
			logger.error("操作keys失败：" + e.getMessage(), e);

		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}

		return result;
	}

	public Long decr(String key) {
		Long result = null;
		Jedis jedis = getJedis();

		try {
			result = jedis.decr(key);
		} catch (Exception e) {
			logger.error("操作keys失败：" + e.getMessage(), e);

		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}

		return result;
	}


	public Long eval(String script, List<String> keys, List<String> args) {
		Long result = null;
		Jedis jedis = getJedis();

		try {
			result = (Long)jedis.eval(script, keys, args);
		} catch (Exception e) {
			logger.error("操作keys失败：" + e.getMessage(), e);

		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}

		return result;

	}

}

